﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.DynamicProxy;
using EntityAop;
using EntityAop.Configuration;
using EntityAop.Interceptors;
using RrslLite.Net.BatchImpl;
using RrslLite.Net.Proxies;
using RrslLite.Net.Proxies;
using RrslLite.Net.BatchImpl;

namespace RrslLite.Configuration
{
    public class BatchBuilderConfiguration
    {
        private static object locker = "locker";

        public virtual BatchCallBuilderConfiguration BatchCallBuilderConfiguration { get; protected set; }
        public virtual EntityAopConfiguration EntityAopProxyGeneratorConfiguration { get; protected set; }

        public virtual string AsyncEndpointConfigurationName { get; protected set; }
        public virtual string DuplexEndpointConfigurationName { get; protected set; }

        public virtual Func<AsyncBatchBuilder> AsyncBatchBuilderCreateFunc { get; protected set; }
        public virtual Func<DuplexBatchBuilder> DuplexBatchBuilderCreateFunc { get; protected set; }

        public virtual Action<AsyncDuplexMessageProcessorProxy> DuplexMessageProcessorFaulted { get; set; }

        public virtual Func<object, ProxyGenerationOptions, IInterceptorWithRelatedTypes[], object> ReceiveProxyGeneratorMethod { get; protected set; }
        public virtual Func<object, object> SendMessageProcess { get; protected set; }

        protected virtual AsyncDuplexMessageProcessorProxy AsyncDuplexMessageProcessorProxyInstance { get; set; }

        public BatchBuilderConfiguration()
        {
            lock (locker)
            {
                Init();
            }
        }

        protected virtual void Init()
        {
            AsyncEndpointConfigurationName = "basicHttp_IWcfMessageService";
            DuplexEndpointConfigurationName = "pollingDuplex_IWcfDuplexMessageService";

            BatchCallBuilderConfiguration = new BatchCallBuilderConfiguration();

            EntityAopProxyGeneratorConfiguration = EntityAopConfiguration.Instance;

            AsyncBatchBuilderCreateFunc = () => new AsyncBatchBuilder();
            DuplexBatchBuilderCreateFunc = () => new DuplexBatchBuilder();

            ReceiveProxyGeneratorMethod =
                (obj, options, interceptors) =>
                {
                    return obj;
                };
        }

        public virtual AsyncMessageProcessorProxy GetAsyncMessageProcessorProxy()
        {
            return new AsyncMessageProcessorProxy(AsyncEndpointConfigurationName);
        }

        public virtual AsyncDuplexMessageProcessorProxy GetDuplexMessageProcessorProxy()
        {
            if (AsyncDuplexMessageProcessorProxyInstance == null)
            {
                AsyncDuplexMessageProcessorProxyInstance = new AsyncDuplexMessageProcessorProxy(DuplexEndpointConfigurationName);
                AsyncDuplexMessageProcessorProxyInstance.Faulted += AsyncDuplexMessageProcessorProxyInstance_Faulted;
            }

            return AsyncDuplexMessageProcessorProxyInstance;
        }

        void AsyncDuplexMessageProcessorProxyInstance_Faulted(object sender, EventArgs e)
        {
            if (DuplexMessageProcessorFaulted != null)
            {
                DuplexMessageProcessorFaulted(AsyncDuplexMessageProcessorProxyInstance);
            }

            AsyncDuplexMessageProcessorProxyInstance = null;
        }

        public virtual BatchBuilderConfiguration WithAsyncEndpointConfigurationName(string endpointConfigurationName)
        {
            AsyncEndpointConfigurationName = endpointConfigurationName;

            return this;
        }

        public virtual BatchBuilderConfiguration WithDuplexEndpointConfigurationName(string endpointConfigurationName)
        {
            DuplexEndpointConfigurationName = endpointConfigurationName;

            return this;
        }

        public virtual BatchBuilderConfiguration WithBatchCallBuilderConfigurationClass(BatchCallBuilderConfiguration batchCallBuilderConfiguration)
        {
            BatchCallBuilderConfiguration = batchCallBuilderConfiguration;

            return this;
        }

        public virtual BatchBuilderConfiguration WithAsyncBatchBuilderCreateFunc(Func<AsyncBatchBuilder> batchBuilderCreateFunc)
        {
            AsyncBatchBuilderCreateFunc = batchBuilderCreateFunc;

            return this;
        }

        public virtual BatchBuilderConfiguration WithDuplexBatchBuilderCreateFunc(Func<DuplexBatchBuilder> batchBuilderCreateFunc)
        {
            DuplexBatchBuilderCreateFunc = batchBuilderCreateFunc;

            return this;
        }

        public AsyncBatchBuilder GetAsyncBatchBuilder()
        {
            return AsyncBatchBuilderCreateFunc();
        }

        public DuplexBatchBuilder GetDuplexBatchBuilder()
        {
            return DuplexBatchBuilderCreateFunc();
        }

        public virtual BatchBuilderConfiguration WithINotifyPropertyChangedInterceptorToReceiveMessages()
        {
            EntityAopProxyGeneratorConfiguration
                .WithMessageReceiveInterceptor(new InstanceSpecificInterceptor())
                .WithMessageReceiveInterceptor(new SetNotifyingInterceptor())
                .WithMessageReceiveInterceptor(new EqualsInterceptor())
                .WithMessageReceiveInterceptor(new TargetAccessorInterceptor())
                .WithProxyGenerationOptions(
                    () => new ProxyGenerationOptions(new ProxyGenerationHook()) { Selector = new InterceptorSelector(), }
                );

            return this;
        }

        public virtual BatchBuilderConfiguration WithCreateClassProxyWithTargetRecursiveToReceiveMessages()
        {
            ReceiveProxyGeneratorMethod = (obj, options, interceptors) =>
            {
                using (var proxyGenerator = EntityAopProxyGeneratorConfiguration.ProxyGeneratorCreateFunc())
                {
                    return proxyGenerator.CreateClassProxyWithTargetRecursive(obj, options, interceptors);
                }
            };

            return this;
        }

        public virtual BatchBuilderConfiguration WithProxyGeneratorMethodForReceive(Func<object, ProxyGenerationOptions, IInterceptorWithRelatedTypes[], object> proxyGeneratorMethod)
        {
            ReceiveProxyGeneratorMethod = proxyGeneratorMethod;

            return this;
        }
    }
}
